home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / appletalk / uab.shar / rtmp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-07-12  |  33.5 KB  |  1,346 lines

  1. static char rcsid[] = "$Author: cck $ $Date: 88/09/18 15:37:30 $";
  2. static char rcsident[] = "$Header: /src/local/mac/cap/etalk/RCS/rtmp.c,v 1.24 88/09/18 15:37:30 cck Rel $";
  3. static char revision[] = "$Revision: 1.24 $";
  4.  
  5. /*
  6.  * rtmp.c: RTMP, ZIP, and NBP gateway protocol modules
  7.  *
  8.  * dropped NBP here because it needs access to routing table
  9.  *
  10.  * Follows specification set in "Inside Appletalk" by Gursharan Sidhu,
  11.  * Richard F. Andrews, and Alan B. Oppenheimer, Apple Computer, Inc.
  12.  * June 1986.
  13.  *
  14.  * Copyright (c) 1988 by The Trustees of Columbia University 
  15.  *  in the City of New York.
  16.  *
  17.  * Permission is granted to any individual or institution to use,
  18.  * copy, or redistribute this software so long as it is not sold for
  19.  * profit, provided that this notice and the original copyright
  20.  * notices are retained.  Columbia University nor the author make no
  21.  * representations about the suitability of this software for any
  22.  * purpose.  It is provided "as is" without express or implied
  23.  * warranty.
  24.  *
  25.  *
  26.  * Edit History:
  27.  *
  28.  *  August, 1988  CCKim Created
  29.  *
  30. */
  31. static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \
  32. Columbia University in the City of New York";
  33.  
  34. #include <stdio.h>
  35. #include <ctype.h>
  36. #include <sys/types.h>
  37. #include <sys/socket.h>
  38. #include <sys/ioctl.h>
  39. #include <sys/uio.h>
  40. #include <sys/time.h>
  41. #include <net/if.h>
  42. #include <netinet/in.h>
  43.  
  44. #include <netat/appletalk.h>
  45. #include "gw.h"
  46. #ifndef OWNHASH
  47. #include <hash.h>
  48. #endif
  49.  
  50. /* stupid function so we can link together items */
  51. struct chain {
  52.   struct chain *c_next;        /* next in list */
  53.   caddr_t c_data;        /* data */
  54. };
  55. /* should be in appletalk.h */
  56. #define ZIP_query 1        /* query type */
  57. #define ZIP_reply 2        /* reply type */
  58. #define ZIP_takedown 3        /* takedown (NYI) */
  59. #define ZIP_bringup 4        /* bringup (NYI) */
  60.  
  61. struct zipddp {            /* ZIP packet */
  62.   byte zip_cmd;
  63.   byte zip_netcount;
  64. };
  65.  
  66. /* zip name as found in zip reply and bringup packets */
  67. struct zipname {
  68.   word zip_net;
  69.   byte zip_len;
  70. };
  71. #define zipNameLen 3
  72.  
  73. /* route states */
  74. #define R_NONE 0        /* or false */
  75. #define R_GOOD 1
  76. #define R_BAD 2
  77. #define R_SUSPECT 3
  78.  
  79. /* messages describing route states (shouldn't change) */
  80. private char *rtmp_state_msg[4] = {
  81.   "deleted",
  82.   "good",
  83.   "bad",
  84.   "suspect"
  85. };
  86.  
  87. #define MAXHOPS 15        /* maximum # of hops a route can have */
  88.  
  89. /* when to "age" route entries */
  90. #define RTMP_VALIDITY_TIMEOUT 20
  91. /* rtmp send timeout: initial is offset from zip timeout */
  92. #define RTMP_INITIAL_SEND_TIMEOUT  30
  93. #define RTMP_SEND_TIMEOUT 10
  94. /* initial zip is for "local" zones */
  95. #define ZIP_INITIAL_TIMEOUT 5
  96. #define ZIP_QUERY_TIMEOUT 10
  97.  
  98. /* maximum number of routes */
  99. #define NROUTES 100
  100.  
  101. private struct route_entry *routes; /* chain of routes */
  102. private caddr_t route_htable_handle; /* route table handle */
  103.  
  104.  
  105. /* bridge node handling stuff */
  106. /* for now (should be hash table or some such)  */
  107. private caddr_t bridgenode_htable_handle;
  108. #define NUMBRNODES 100
  109. struct bridge_node {        /* bridge node entry */
  110.   NODE id;
  111.   PORT_T port;
  112. };
  113.  
  114. struct bridge_node_key {    /* structure to pass a key in */
  115.   NODE *idp;
  116.   PORT_T port;
  117. };
  118.  
  119. private int zone_unknown = 0;    /* a zone is unknown */
  120.  
  121. /* same as pstr */
  122. private caddr_t zone_hash_table;
  123. #define NZONES 50        /* random, need not be too big */
  124. private struct chain *zonelist;    /* chain of zones */
  125.  
  126. private int m_route = 0;
  127. private int m_bnode = 0;
  128. private int m_cnode = 0;
  129. private int m_zone = 0;
  130.  
  131. export void rtmp_init();
  132. export void rtmp_start();
  133. private int route_compare();
  134. private caddr_t route_alloc();
  135. private u_int route_compress();
  136. private void routes_init();
  137. export struct route_entry *route_find();
  138. private struct route_entry *route_create();
  139. private void route_delete();
  140. export struct route_entry *route_list_start();
  141. export struct route_entry *route_next();
  142. export int route_add_host_entry();
  143. export char *node_format();
  144. private int bstrcmp();
  145. private int bridgenode_compare();
  146. private caddr_t bridgenode_alloc();
  147. private u_int bridgenode_compress();
  148. private void bridgenode_init();
  149. private NODE *bridgenode_find();
  150. private boolean rtmp_handler();
  151. private void rtmp_dump_entry();
  152. private void rtmp_dump_entry_to_file();
  153. private int rtmp_send_timeout();
  154. private void rtmp_send();
  155. private int rtmp_validity_timeout();
  156. private void rtmp_replace_entry();
  157. private void rtmp_update_entry();
  158. private boolean rtmprq_handler();
  159. private boolean zip_handler();
  160. private int zip_query_timeout();
  161. private int zip_query_handler();
  162. private int zip_reply_handler();
  163. private int zip_atp_handler();
  164. /* private int zone_hash(); */
  165. private caddr_t zone_alloc();
  166. private int pstrc();
  167. private int zone_compare();
  168. private u_int zone_compress();
  169. private void zone_init();
  170. export byte *zone_find();
  171.  
  172. private void rtmp_format_hash_stats();
  173. export void rtmp_dump_stats();
  174. export void rtmp_dump_table();
  175.  
  176. /*
  177.  * initialize
  178.  * 
  179.  * clear up vars
  180. */
  181. export void
  182. rtmp_init()
  183. {
  184.   routes_init();
  185.   bridgenode_init();
  186.   zone_init();
  187. }
  188.  
  189.  
  190. /*
  191.  * rtmp start - fires up the timers 
  192.  *
  193. */
  194. export void
  195. rtmp_start()
  196. {
  197.   struct timeval tv;
  198.   tv.tv_sec = RTMP_VALIDITY_TIMEOUT; /* 20 second validity timer */
  199.   tv.tv_usec = 0;
  200.   relTimeout(rtmp_validity_timeout, 0, &tv, TRUE);
  201.   /* these last two could be combined */
  202.   tv.tv_sec = RTMP_INITIAL_SEND_TIMEOUT;
  203.   tv.tv_usec = 0;
  204.   relTimeout(rtmp_send_timeout, 0, &tv, TRUE);
  205.   tv.tv_sec = ZIP_INITIAL_TIMEOUT;
  206.   tv.tv_usec = 0;
  207.   relTimeout(zip_query_timeout, 0, &tv, TRUE);
  208. }
  209.  
  210. /* routing table handler */
  211. /*
  212.  * compare key net to route entry
  213.  *
  214. */
  215. private int
  216. route_compare(net,re)
  217. word *net;
  218. struct route_entry *re;
  219. {
  220.   return(((int)*net) - ((int)(re->re_ddp_net)));
  221. }
  222.  
  223. /*
  224.  * allocate data: create a route
  225.  *
  226.  *
  227. */
  228. private caddr_t
  229. route_alloc(net)
  230. word *net;
  231. {
  232.   struct route_entry *re;
  233.  
  234.   if ((re = (struct route_entry *)malloc(sizeof(struct route_entry))) == NULL)
  235.     return(NULL);
  236.   m_route++;
  237.   re->re_ddp_net = *net;    /* copy in network */
  238.   re->re_state = R_NONE;    /* set state to none */
  239.   re->re_next = routes;
  240.   routes = re;            /* link to head */
  241.   return((caddr_t)re);        /* and return */
  242. }
  243.  
  244. /*
  245.  * compress key to u_int
  246.  *
  247. */
  248. private u_int
  249. route_compress(net)
  250. word *net;
  251. {
  252.   return(*net);
  253. }
  254.  
  255. private void
  256. routes_init()
  257. {
  258.   routes = NULL;        /* no routes */
  259.   route_htable_handle =
  260.     h_new(HASH_POLICY_CHAIN, HASH_TYPE_MULTIPLICATIVE, NROUTES,
  261.       route_compare, route_alloc, route_compress, NULL, NULL, NULL);
  262.   ddp_open(rtmpSkt, rtmp_handler);
  263. }
  264. /*
  265.  * find route for a particular network
  266.  *
  267. */
  268. export
  269. struct route_entry *
  270. route_find(net)
  271. word net;
  272. {
  273.   struct route_entry *re;
  274.   re = (struct route_entry *)h_member(route_htable_handle, &net);
  275.   if (re && re->re_state)    /* we don't really delete */
  276.     return(re);
  277.   return(NULL);
  278. }
  279.  
  280. /*
  281.  * create a routing entry and initialize it.
  282.  *
  283. */
  284. private struct route_entry *
  285. route_create(net)
  286. word net;
  287. {
  288.   register struct route_entry *re;
  289.   int d, b;
  290.  
  291.   re = (struct route_entry *)
  292.     h_operation(HASH_OP_INSERT, route_htable_handle, &net, -1,-1,&d,&b);
  293.  
  294.   if (re == NULL)        /* should not happen, but. */
  295.     return(NULL);
  296.   log(LOG_LOTS, "new route for net %d.%d at hash [bkt %d, d %d]\n",
  297.      nkipnetnumber(net),nkipsubnetnumber(net),
  298.      b,d);
  299.   zone_unknown++;        /* new route, so set */
  300.   re->re_dist = 0;        /* assume zero distance */
  301.   re->re_bridgeid_p = NULL;    /* means self */
  302. /*  re->re_ddp_net = net; */ /* done already */
  303.   re->re_zip_taken = FALSE;    /* make sure */
  304.   re->re_zonep = NULL;        /* make sure */
  305.   return(re);
  306. }
  307.  
  308. /* delete route - for now just set state to none */
  309. /* may want to time it out at some point */
  310. private void
  311. route_delete(re)
  312. struct route_entry *re;
  313. {
  314.   re->re_state = R_NONE;
  315. }
  316.  
  317.  
  318. /* return route list start */
  319. export struct route_entry *
  320. route_list_start()
  321. {
  322.   return(routes);
  323. }
  324.  
  325. /* get next in list: hidden in case we want to change way done */
  326. export struct route_entry *
  327. route_next(re)
  328. struct route_entry *re;
  329. {
  330.   return(re->re_next);
  331. }
  332.  
  333. /*
  334.  * establish a new port
  335.  *
  336.  * net in network format (8 bits - word)
  337.  * node as bytes (variable length, network order, zero padded in front)
  338.  * nodesize - number of bits valid in node
  339.  * ddp_node
  340.  * zone to set if any
  341.  *
  342. */
  343. export
  344. route_add_host_entry(port, net, zone)
  345. PORT_T port;
  346. word net;
  347. byte *zone;
  348. {
  349.   struct route_entry *re;
  350.  
  351.   if (net == 0)
  352.     return(-1);
  353.   /* if network given, then construct internal route */
  354.   /* do a find in case route already acquired */
  355.   if ((re = route_find(net)) == NULL)
  356.     if ((re = route_create(net)) == NULL)
  357.       return(-1);
  358.   /* reset or set */
  359.   re->re_state = R_GOOD;
  360.   re->re_port = port;
  361.   re->re_dist = 0;
  362.   re->re_bridgeid_p = NULL;
  363.   log(LOG_BASE, "port %d host route added for network %d.%d",
  364.       port, nkipnetnumber(net), nkipsubnetnumber(net));
  365.   rtmp_dump_entry("host port", re);
  366.   if (zone == NULL)
  367.     return(0);
  368.   /* if zone given for net, then add it */
  369.   re->re_zonep = zone_find(zone, TRUE);    /* insert zone name */
  370.   if (re->re_zonep && zone_unknown > 0) {
  371.     log(LOG_BASE, "port %d zone name %s inserted", port, re->re_zonep+1);
  372.     zone_unknown--;
  373.   }
  374.   return(0);
  375. }
  376.  
  377.  
  378.  
  379. /*
  380.  * format node structure for printing
  381.  *
  382. */
  383. export char *
  384. node_format(node)
  385. NODE *node;
  386. {
  387.   static char tmpbuf[200];
  388.   static char *fmtstr = "%x%x%x%x%x%x%x%x";
  389.   byte *id;
  390.   int n;
  391.  
  392.   if (node == NULL)
  393.     return("self");
  394.   id = node->n_id;
  395.   /* if less than 5 bytes, convert to network order int and print */
  396.   switch (node->n_bytes) {
  397.   case 4:
  398.     n = ntohl((id[0]<<24)|(id[1]<<16)|(id[2]<<8)|id[3]);
  399.     break;
  400.   case 3:
  401.     n = ntohl((id[0]<<16)|(id[1]<<8)|id[2]);
  402.     break;
  403.   case 2:
  404.     n = ntohs(id[0]<<8|id[1]);
  405.     break;
  406.   case 1:
  407.     n = id[0];
  408.     break;
  409.   default:
  410.     sprintf(tmpbuf, fmtstr+2*(MAXNODEBYTE-node->n_bytes),
  411.         node->n_id[0], node->n_id[1],
  412.       node->n_id[2], node->n_id[3],
  413.       node->n_id[4], node->n_id[5],
  414.       node->n_id[6], node->n_id[7]);
  415.     return(tmpbuf);
  416.   }
  417.   sprintf(tmpbuf, "%d", n);
  418.   return(tmpbuf);
  419. }
  420.  
  421. /* like strncmp, but allows "0" bytes */
  422. private int
  423. bstrcmp(a,b,l)
  424. register byte *a;
  425. register byte *b;
  426. register int l;
  427. {
  428.   register int c = 0;        /* if zero length, then same */
  429.  
  430.   while (l--)             /* while data */
  431.     if ((c = (*a++ - *b++)))    /* compare and get difference */
  432.       break;            /* return c */
  433.   return(c);            /* return value */
  434. }
  435.  
  436. /* bridge node table handler */
  437.  
  438. /* compare (port,node) to bridgenode */
  439. private int
  440. bridgenode_compare(k, bn)
  441. struct bridge_node_key *k;
  442. struct bridge_node *bn;
  443. {
  444.   if (k->port != bn->port)
  445.     return((int)(k->port - bn->port));
  446.   if (k->idp->n_size != bn->id.n_size)
  447.     return(k->idp->n_size - bn->id.n_size);
  448.   return(bstrcmp((caddr_t)k->idp->n_id, (caddr_t)bn->id.n_id, bn->id.n_bytes));
  449. }
  450.  
  451. /* allocate space for a bridge node */
  452. private caddr_t
  453. bridgenode_alloc(k)
  454. struct bridge_node_key *k;
  455. {
  456.   struct bridge_node *bn;
  457.  
  458.   if ((bn = (struct bridge_node *)malloc(sizeof(struct bridge_node))) == NULL)
  459.     return(NULL);
  460.   m_bnode++;
  461. #ifdef DEBUG
  462.   log(0, "BRIDGE NODE CREATE");
  463.   log(0, "PORT %d", k->port);
  464.   log(0, "ID len %d, byte 0 %d", k->idp->n_size, k->idp->n_id[0]);
  465. #endif
  466.   bn->id = *k->idp;        /* copy in */
  467.   bn->port = k->port;
  468.   return((caddr_t)bn);
  469. }
  470.  
  471. /* compress key  to an u_int */
  472. private u_int
  473. bridgenode_compress(k)
  474. struct bridge_node_key *k;
  475. {
  476.   u_int r = (u_int)k->port;
  477.   int i = k->idp->n_bytes;    /* # of bytes */
  478.   byte *p = k->idp->n_id;    /* data */
  479.     
  480.   /* add in p, but keep rotating r */
  481.   r ^= k->idp->n_size;        /* xor size in */
  482.   while (i--)
  483.     r = ((r>>1)|(r<<31)) + *p++;
  484.   return(r);
  485. }
  486.  
  487.  
  488. /* initialize */
  489. /* should we limit the # of bridge nodes by using a open hash? */
  490. private void
  491. bridgenode_init()
  492. {
  493.   bridgenode_htable_handle =
  494.     h_new(HASH_POLICY_CHAIN, HASH_TYPE_MULTIPLICATIVE, NUMBRNODES,
  495.       bridgenode_compare, bridgenode_alloc,
  496.       bridgenode_compress, NULL, NULL, NULL);
  497. }
  498.  
  499. /* find a bridge node based on port,node key */
  500. private NODE *
  501. bridgenode_find(port, node)
  502. PORT_T port;
  503. NODE *node;
  504. {
  505.   struct bridge_node *bn;
  506.   struct bridge_node_key bk;
  507.   int d,b;
  508.  
  509.   bk.idp = node;
  510.   bk.port = port;
  511.  
  512.   bn = (struct bridge_node *)
  513.     h_operation(HASH_OP_INSERT, bridgenode_htable_handle, &bk, -1,-1,&d,&b);
  514.  
  515.   if (bn == NULL)
  516.     return(NULL);
  517. #ifdef DEBUG
  518.   printf("look bridge node %s at hash [bkt %d, d %d]\n",
  519.      node_format(node), b,d);
  520. #endif
  521.   return(&bn->id);        /* return node id */
  522. }
  523.  
  524. /* RTMP handling */
  525.  
  526. /*
  527.  * handle incoming rtmp packets
  528.  *
  529. */
  530. /*ARGSUSED*/
  531. private boolean
  532. rtmp_handler(port, ddp, data, len)
  533. PORT_T port;
  534. DDP *ddp;            /* not used */
  535. byte *data;
  536. int len;
  537. {
  538.   struct route_entry *re;
  539.   RTMPtuple tuple;
  540.   word net;
  541.   NODE id, *sid;
  542.  
  543.   if (ddp->type == ddpRTMPRQ)    /* is it a rtmp request? */
  544.     return(rtmprq_handler(port,ddp)); /* yes, handle it */
  545.   if (ddp->type != ddpRTMP)    /* is it rtmp? */
  546.     return(TRUE);        /* no, dump it */
  547.   if (len < sizeof(net))    /* rtmpSize */
  548.     return(TRUE);
  549.   /* get net out */
  550.   bcopy((caddr_t)data, (caddr_t)&net, sizeof(net));
  551.   len -= sizeof(net);
  552.   data += sizeof(net);
  553.   if (len < 1)            /* id len */
  554.     return(TRUE);
  555.   id.n_size = *data;        /* get id length */
  556.   id.n_bytes = (id.n_size + 7) / 8; /* make into bytes */
  557.   if (len < (id.n_bytes+1))    /* id len + id */
  558.     return;
  559.   bcopy((caddr_t)data+1, (caddr_t)id.n_id, id.n_bytes);    /* copy id */
  560.   len -= (id.n_bytes + 1);    /* reduce */
  561.   data += (id.n_bytes + 1);    /* reduce */
  562.   
  563.   sid = bridgenode_find(port, &id); /* canonicalize */
  564.   if (sid == NULL)        /* ourselves or no room */
  565.     return(TRUE);
  566.   log(LOG_BASE, "NEW RTMP: port %d, source %s", port, node_format(sid));
  567.  
  568.   if (!PORT_NET_READY(port, net, sid))
  569.     route_add_host_entry(port, net, NULL); /* zone isn't known yet! */
  570.  
  571.   /* use tuplesize because of byte alignment problems */
  572.   while (len >= rtmpTupleSize) {
  573.     bcopy((caddr_t)data, (caddr_t)&tuple, rtmpTupleSize);
  574.     data += rtmpTupleSize, len -= rtmpTupleSize;
  575.     re = route_find(tuple.net); /* get entry if any */
  576.     if (re)             /* update */
  577.       rtmp_update_entry(re, port, sid, &tuple);
  578.     else {             /* create */
  579.       re = route_create(tuple.net);
  580.       if (!re)
  581.     continue;
  582.       log(LOG_LOTS, "create_entry: net %d.%d",
  583.            nkipnetnumber(tuple.net),
  584.            nkipsubnetnumber(tuple.net));
  585.       rtmp_replace_entry(re, port, sid, &tuple, FALSE);
  586.     }
  587.   }
  588.   return(TRUE);
  589. }
  590.  
  591.  
  592. /*
  593.  * dump rtmp table entry nicely
  594.  *
  595. */
  596. private void
  597. rtmp_dump_entry(msg, re)
  598. char *msg;
  599. struct route_entry *re;
  600. {
  601.   if (!re->re_state)
  602.     return;
  603.   /* fixup */
  604.   log(LOG_LOTS, "%s: net %d.%d, bridge %s, dist %d, port %d, state %s",
  605.       msg, nkipnetnumber(re->re_ddp_net), nkipsubnetnumber(re->re_ddp_net),
  606.       node_format(re->re_bridgeid_p), re->re_dist, re->re_port,
  607.       rtmp_state_msg[re->re_state]);
  608. }
  609.  
  610. private void
  611. rtmp_dump_entry_to_file(fd, re)
  612. FILE *fd;
  613. struct route_entry *re;
  614. {
  615.   if (!re->re_state)
  616.     return;
  617.   /* fixup */
  618. fprintf(fd, " net %d.%d, bridge %s, dist %d, port %d, state %s, zone %d-%s\n",
  619.       nkipnetnumber(re->re_ddp_net), nkipsubnetnumber(re->re_ddp_net),
  620.       node_format(re->re_bridgeid_p), re->re_dist, re->re_port,
  621.       rtmp_state_msg[re->re_state], 
  622.       (re->re_zonep ? *re->re_zonep : 0),
  623.       (re->re_zonep ? ((char *)re->re_zonep+1) : (char *)"<unknown>"));
  624. }
  625.  
  626. /*
  627.  * timeout: rtmp send - broadcast rtmp's
  628.  * 
  629. */
  630. private int
  631. rtmp_send_timeout()
  632. {
  633.   PORT_T port;
  634.   register struct route_entry *re;
  635.   struct timeval tv;
  636.   RTMP *rtmp;
  637.   RTMPtuple tuple;
  638.   char buf[ddpMaxData];
  639.   char *p;
  640.   int count, rest;
  641.   
  642.   rtmp = (RTMP *)buf;
  643.   re = routes;
  644.   /* load up rtmp entry */
  645.   do {
  646.     p = buf + sizeof(RTMP);    /* point into buf */
  647.     rest = ddpMaxData - sizeof(RTMP);
  648.     /* while room in packet */
  649.     for (count = 0; re && rest >= rtmpTupleSize; re = re->re_next) {
  650.       if (re->re_state != R_GOOD && re->re_state != R_SUSPECT)
  651.     continue;        /* not good or suspect */
  652.       if (re->re_zip_taken)    /* in zip takedown */
  653.     continue;
  654.       /* strict: don't send out updates for routes we don't know */
  655.       /* the zone for -- this way bad data may go away */
  656.       if (re->re_zonep == NULL)
  657.     continue;
  658.       if (!(re->re_dist < MAXHOPS))
  659.     continue;
  660.       tuple.net = re->re_ddp_net;
  661.       tuple.hops = re->re_dist;
  662.       bcopy((caddr_t)&tuple, p, rtmpTupleSize);
  663.       count++;            /* found another */
  664.       p += rtmpTupleSize;
  665.       rest -= rtmpTupleSize;
  666.     }
  667.     for (port = PORT_LIST_START(); port != NULL; port = PORT_NEXT(port)) {
  668.       NODE *pid;
  669.  
  670.       if (!PORT_ISBRIDGING(port))
  671.     continue;
  672.       if (!PORT_READY(port))
  673.     continue;
  674.       rtmp->net = PORT_DDPNET(port);
  675.       pid = PORT_NODEID(port);
  676.       rtmp->idLen = pid->n_size;
  677.       bcopy((caddr_t)pid->n_id, (caddr_t)&rtmp->id, pid->n_bytes);
  678.       rtmp_send(rtmp, 3+pid->n_bytes, count, rtmp->net, DDP_BROADCAST_NODE);
  679.     }
  680.   } while (re);            /* still routes */
  681.   tv.tv_sec = RTMP_SEND_TIMEOUT; /* 10 second send timer */
  682.   tv.tv_usec = 0;
  683.   relTimeout(rtmp_send_timeout, 0, &tv, TRUE);
  684. }
  685.  
  686. /*
  687.  * send the rtmp packet on the specified port
  688.  *
  689. */
  690. private void
  691. rtmp_send(rtmp, rtmp_size, count, dstnet, dst)
  692. RTMP *rtmp;
  693. int rtmp_size;
  694. int count;
  695. word dstnet;
  696. byte dst;
  697. {
  698.   DDP rddp;        /* reply ddp header */
  699.   int dlen = rtmp_size+rtmpTupleSize*count;
  700.  
  701.   rddp.srcSkt = rtmpSkt;
  702.   rddp.dstNet = dstnet;
  703.   rddp.dstNode = dst;
  704.   rddp.dstSkt = rtmpSkt;
  705.   rddp.type = ddpRTMP;
  706.   ddp_output(NULL, &rddp, rtmp, dlen);
  707. }
  708.  
  709. /*
  710.  * timeout: rtmp validity
  711.  *
  712.  * run timer on rtmp validity
  713. */
  714. private int
  715. rtmp_validity_timeout()
  716. {
  717.   register struct route_entry *re;
  718.   struct timeval tv;
  719.   for (re = routes; re; re = re->re_next) {
  720.     switch (re->re_state) {
  721.     case R_GOOD:
  722.       if (re->re_dist != 0)
  723.     re->re_state = R_SUSPECT;
  724.       break;
  725.     case R_SUSPECT:
  726.       rtmp_dump_entry("route went bad", re);
  727.       re->re_state = R_BAD;
  728.       break;
  729.     case R_BAD:
  730.       rtmp_dump_entry("route deleted", re);
  731.       route_delete(re);
  732.       break;
  733.     }
  734.   }
  735.   tv.tv_sec = RTMP_VALIDITY_TIMEOUT; /* 20 second validity timer */
  736.   tv.tv_usec = 0;
  737.  
  738.   relTimeout(rtmp_validity_timeout, 0, &tv, TRUE);
  739. }
  740.  
  741. /*
  742.  * rtmp_replace_entry: replace or add a route
  743.  *
  744.  * if istickler is set then this is a tickler packet that ensures
  745.  * route stays good
  746.  *
  747. */
  748. private void
  749. rtmp_replace_entry(re, port, sid, tuple, istickler)
  750. struct route_entry *re;
  751. PORT_T port;            /* source port */
  752. NODE *sid;            /* source id */
  753. RTMPtuple *tuple;
  754. int istickler;            /* true if this replace should be */
  755.                 /* considered "a tickle" */
  756. {
  757.   int rewasthere = re->re_state;
  758.  
  759.   /* dump won't do anything if no state */
  760.   if (rewasthere && !istickler)
  761.     rtmp_dump_entry("replacing entry", re);
  762.   re->re_dist = tuple->hops + 1;
  763.   re->re_bridgeid_p = sid;
  764.   re->re_port = port;
  765.   re->re_state = R_GOOD;
  766.   if (!istickler)
  767.     rtmp_dump_entry(rewasthere ? "replaced entry" : "new" , re);
  768. }
  769.  
  770. /*
  771.  * rtmp_update_entry - figure out whether the route should be updated 
  772.  *  or not
  773.  *
  774. */
  775. private void
  776. rtmp_update_entry(re, port, sid,  tuple)
  777. struct route_entry *re;
  778. PORT_T port;            /* source port */
  779. NODE *sid;            /* source id */
  780. RTMPtuple *tuple;
  781. {
  782.   if (re->re_state == R_BAD && tuple->hops < MAXHOPS) { /* replace entry */
  783.     log(LOG_LOTS, "update_entry: net %d.%d, replacing because bad", 
  784.     nkipnetnumber(tuple->net),
  785.     nkipsubnetnumber(tuple->net));
  786.     rtmp_replace_entry(re, port, sid, tuple, FALSE);
  787.     return;
  788.   }
  789.   if (tuple->hops < MAXHOPS && re->re_dist > tuple->hops) {
  790.     int istickler; 
  791.     istickler = (re->re_dist == (tuple->hops+1));
  792.     if (!istickler) {
  793.       /* if not simple case of updating bad point */
  794.       log(LOG_LOTS, "update_entry: net %d.%d, replacing because better route",
  795.       nkipnetnumber(tuple->net),
  796.       nkipsubnetnumber(tuple->net));
  797.     }
  798.     rtmp_replace_entry(re, port, sid, tuple, istickler);
  799.     return;
  800.   }
  801.   /* know we know that hops >= 15 or re->re_dist <= tuple->hops */
  802.   /* if re's bridge matches the rmtp source bridge */
  803.   /* and in on the same port, then the network is futher away... */
  804.   if (re->re_bridgeid_p == sid && re->re_port == port) {
  805.     re->re_dist++;
  806.     if ((re->re_dist) <= MAXHOPS) {
  807.       re->re_state = R_GOOD;
  808.       rtmp_dump_entry("hop count increased", re);
  809.     } else {
  810.       rtmp_dump_entry("too many hops", re);
  811.       route_delete(re);
  812.     }
  813.   }
  814. }
  815.  
  816.  
  817. /*
  818.  * handle incoming rtmp request packet
  819.  *
  820. */
  821. private int
  822. rtmprq_handler(port, ddp)
  823. PORT_T port;
  824. DDP *ddp;
  825. {
  826.   RTMP rtmp;
  827.   NODE *pid;
  828.  
  829.   if (!PORT_ISBRIDGING(port))    /* not full bridge */
  830.     return(TRUE);        /* so, don't advertise */
  831.   if (!PORT_READY(port))    /* port isn't fully setup */
  832.     return(TRUE);
  833.   /* respond with data about the port */
  834.   rtmp.net = PORT_DDPNET(port);
  835.   pid = PORT_NODEID(port);
  836.   rtmp.idLen = pid->n_size;
  837.   bcopy((caddr_t)pid->n_id, (caddr_t)&rtmp.id, pid->n_bytes);
  838.   /* no tuples */
  839.   rtmp_send(&rtmp, 3+pid->n_bytes, 0, ddp->srcNet, ddp->srcNode);
  840.   return(TRUE);
  841. }
  842.  
  843. /*
  844.  * handle incoming DDP zip packets 
  845. */
  846. private boolean
  847. zip_handler(port, ddp, data, datalen)
  848. PORT_T port;
  849. DDP *ddp;
  850. byte *data;
  851. int datalen;
  852. {
  853.   struct zipddp zd;
  854.  
  855.   if (ddp->type == ddpATP)    /* atp? */
  856.     return(zip_atp_handler(port, ddp, data, datalen)); /* yes */
  857.  
  858.   if (ddp->type != ddpZIP)    /* zip? */
  859.     return(TRUE);        /* no, dump it */
  860.  
  861.   if (datalen < sizeof(zd))
  862.     return(TRUE);
  863.   bcopy((caddr_t)data, (caddr_t)&zd, sizeof(zd)); /* get zip header */
  864.   datalen -= sizeof(zd), data += sizeof(zd);
  865.  
  866.   switch (zd.zip_cmd) {
  867.   case ZIP_query:
  868.     zip_query_handler(zd.zip_netcount, port, ddp, data, datalen);
  869.     break;
  870.   case ZIP_reply:
  871.     zip_reply_handler(zd.zip_netcount, port, ddp, data, datalen);
  872.     break;
  873.   case ZIP_takedown:
  874.     break;
  875.   case ZIP_bringup:
  876.     break;
  877.   }
  878.   return(TRUE);
  879. }
  880.  
  881. /*
  882.  * ZIP timeout
  883.  *
  884.  * query routes with unkown zones
  885.  *
  886.  *  algorithm: send zip query to bridge that sent us the rtmp.
  887.  *  try to enclose as many networks as possible in the query
  888.  *
  889. */
  890. private int
  891. zip_query_timeout()
  892. {
  893.   DDP ddp;
  894.   struct route_entry *re;
  895.   struct route_entry *first;
  896.   PORT_T port;
  897.   int first_idx;
  898.   int j;
  899.   NODE *curbridge;
  900.   struct zipddp *zd;
  901.   word zip_buf[ddpMaxData / sizeof(word)];
  902.   int count;
  903.   int mainnotknown = FALSE;
  904.  
  905.   if (!zone_unknown)        /* set whenever new route is created */
  906.     return;
  907.   /* initialize helper field, find first to query */
  908.   for (first=NULL, re = routes, j = 0; re ; re = re->re_next)
  909.     if (re->re_state && !re->re_zonep) {
  910.       if (!first) {        /* remember first */
  911.     first = re;
  912.       }
  913.       if (re->re_bridgeid_p == NULL) {
  914.     if (!mainnotknown) {
  915.       first = re;        /* reset first one to do */
  916.       mainnotknown = TRUE;    /* don't know zone of a main interface */
  917.     }
  918.       }
  919.       re->re_zip_helper = 0;
  920.       j++;            /* mark work */
  921.     }
  922.   if (j == 0)
  923.     zone_unknown = FALSE;
  924.  
  925.   /* query the various bridges */
  926.   while (j && first) {
  927.     curbridge = first->re_bridgeid_p; /* bridge id pointer */
  928.     port = first->re_port;    /* get port for this bridge */
  929.     count = 1;            /* skip first word */
  930.     re = first;            /* this is where to start */
  931.     first = NULL;        /* reset start point */
  932.     for (; re ;  re = re->re_next) {
  933.       /* skip if main interf. not known */
  934.       if (mainnotknown && re->re_bridgeid_p) 
  935.     continue;        /* and not local interface */
  936.       if (!re->re_state || re->re_zonep || re->re_zip_helper) /* ignore */
  937.     continue;
  938.       if (re->re_bridgeid_p == curbridge &&
  939.       (count < (ddpMaxData / sizeof(word)))) {
  940.     j--;
  941.     re->re_zip_helper = 1;    /* mark  */
  942.     zip_buf[count++] = re->re_ddp_net; /* to get */
  943.     log(LOG_LOTS, "will zip %s for %d.%d", node_format(curbridge), 
  944.         nkipnetnumber(re->re_ddp_net), nkipsubnetnumber(re->re_ddp_net));
  945.       } else if (!first) {    /* remember next in sequence */
  946.       first = re;        /* set first */
  947.     }
  948.     }
  949.     if (count == 1)        /* something wierd happened */
  950.       continue;
  951.     zd = (struct zipddp *)zip_buf;
  952.     zd->zip_cmd = ZIP_query;
  953.     zd->zip_netcount = count - 1;
  954.     ddp.dstNet = PORT_DDPNET(port);
  955.     /* either don't care (because will be short ddp) or broadcast */
  956.     /* actually need reverse mapping (port,node) to ddp node */
  957.     ddp.dstNode = curbridge ? 0 : DDP_BROADCAST_NODE;
  958.     ddp.dstSkt = zipZIS;
  959.     ddp.srcSkt = zipZIS;
  960.     ddp.type = ddpZIP;
  961.     log(LOG_LOTS, "zipping %s for %d networks", node_format(curbridge),
  962.     count-1);
  963.     ddp_output(curbridge, &ddp, zip_buf, count*sizeof(word));
  964.   }
  965.   /* restart query */
  966.   { struct timeval tv; 
  967.     tv.tv_sec = ZIP_QUERY_TIMEOUT; 
  968.     tv.tv_usec = 0;
  969.  
  970.     relTimeout(zip_query_timeout, 0, &tv, TRUE);
  971.   }
  972. }
  973.  
  974.  
  975. /*
  976.  * zip_query_handler: handle an incoming zip query packet
  977.  *
  978. */
  979. private int
  980. zip_query_handler(count, port, ddp, data, datalen)
  981. int count;
  982. PORT_T port;
  983. DDP *ddp;
  984. byte *data;
  985. int datalen;
  986. {
  987.   struct route_entry *re;
  988.   DDP sddp;
  989.   byte buf[ddpMaxData];
  990.   int slen, i, zl;
  991.   byte *p;
  992.   word net;
  993.   struct zipddp *zd;
  994.  
  995.   p = buf;
  996.   p += sizeof(struct zipddp);
  997.   slen = sizeof(struct zipddp);
  998.   zd = (struct zipddp *)buf;
  999.   zd->zip_cmd = ZIP_reply;    /* set command */
  1000.   zd->zip_netcount = 0;        /* zero nets in response as yet */
  1001.   /* best effort -- fit as many as possible, but don't bother with */
  1002.   /* multiple replies -- not clear remote would handle anyway */
  1003.   while (count--) {
  1004.     if (datalen < sizeof(net))    /* any data left? */
  1005.       break;            /* no, count is wrong, stop! */
  1006.     bcopy((caddr_t)data, (caddr_t)&net, sizeof(net));
  1007.     datalen -= sizeof(net), data += sizeof(net);
  1008.     if ((re = route_find(net)) == NULL)    /* no route skip */
  1009.       continue;
  1010.     if (!re->re_zonep)
  1011.       continue;
  1012.     i = ((*re->re_zonep) + 1 + sizeof(word));
  1013.     if ((slen + i) > ddpMaxData)
  1014.       break;
  1015.     /* copy in response data */
  1016.     bcopy((caddr_t)&net, (caddr_t)p, sizeof(net));
  1017.     p += sizeof(net);
  1018.     zl = *re->re_zonep + 1;        /* get zone length */
  1019.     bcopy((caddr_t)re->re_zonep, (caddr_t)p, zl); /* copy zone name */
  1020.     p += zl;
  1021.     zd->zip_netcount++;        /* increment count */
  1022.     log(LOG_JUNK, "query on net %d.%d yields zone %s", 
  1023.     nkipnetnumber(net), nkipsubnetnumber(net), re->re_zonep+1);
  1024.     slen += i;
  1025.   }
  1026.   sddp.dstNet = ddp->srcNet;
  1027.   sddp.dstNode = ddp->srcNode;
  1028.   sddp.dstSkt = ddp->srcSkt;
  1029.   sddp.srcSkt = zipZIS;
  1030.   sddp.type = ddpZIP;
  1031.   ddp_output(NULL, &sddp, buf, slen);
  1032. }
  1033.  
  1034. /*
  1035.  * handle incoming zip reply.  basically insert zone names
  1036.  * into the table if possible
  1037.  *
  1038. */
  1039. private int
  1040. zip_reply_handler(count, port, ddp, data, datalen)
  1041. int count;
  1042. PORT_T port;
  1043. DDP *ddp;
  1044. byte *data;
  1045. int datalen;
  1046. {
  1047.   word net;
  1048.   struct route_entry *re;
  1049.   byte *p = data;
  1050.   byte *pp, *zone;
  1051.   int zonelen;
  1052.  
  1053.   while (count--) {
  1054.     if (datalen < (1+sizeof(net)))
  1055.       break;
  1056.     bcopy((caddr_t)p, (caddr_t)&net, sizeof(net)); /* get zone information */
  1057.     p += sizeof(net);        /* move to the name */
  1058.     datalen -= sizeof(net);
  1059.     zonelen = 1 + *p;
  1060.     /* now p points to a pstr */
  1061.     if (datalen < zonelen)    /* no data left? */
  1062.       break;
  1063.     zone = p;            /* p now points to zone */
  1064.     p += zonelen;
  1065.     datalen -= zonelen;
  1066.     if ((re = route_find(net)) == NULL)
  1067.       continue;
  1068.     pp = (byte *)zone_find(zone, TRUE); /* find or insert zone name */
  1069.     if (pp == NULL) {
  1070.       log(LOG_BASE, "ZIP: no room for insert for zone\n");
  1071.       continue;
  1072.     }
  1073.     if (re->re_zonep) {        /* zone already known for net */
  1074.       if (pp && pp != re->re_zonep) {
  1075.     log(LOG_BASE, "zone name conflict for %d.%d, received %s had %s\n",
  1076.         nkipnetnumber(re->re_ddp_net), nkipsubnetnumber(re->re_ddp_net),
  1077.         pp+1, re->re_zonep+1);
  1078.       }
  1079.       continue;
  1080.     }
  1081.     /* must have zone for this route (which didn't have one before) */
  1082.     re->re_zonep = pp;    /* mark zone */
  1083.     /* if zone is known for primary route, say so */
  1084.     if (re->re_bridgeid_p == NULL && re->re_ddp_net == PORT_DDPNET(port))
  1085.       PORT_ZONE_KNOWN(port, re->re_zonep);
  1086.     log(LOG_BASE, "ZIPPED: from %d.%d.%d network %d.%d for zone %s",
  1087.     nkipnetnumber(ddp->srcNet), nkipsubnetnumber(ddp->srcNet),
  1088.     ddp->srcNode,
  1089.     nkipnetnumber(re->re_ddp_net), nkipsubnetnumber(re->re_ddp_net),
  1090.     re->re_zonep+1);
  1091.   }
  1092. }
  1093.  
  1094. /*
  1095.  * handle a zip atp command: Get Zone List or Get My Zone 
  1096.  *
  1097.  *
  1098. */
  1099. /* these are only defined in abatp.h not appletalk.h (because nobody) */
  1100. /* should need such fine control under normal cirucmstances */
  1101. /* put the ifndefs around in case we decide to move them one day */
  1102. #ifndef atpCodeMask
  1103. # define atpCodeMask 0xc0
  1104. #endif
  1105. #ifndef atpReqCode
  1106. # define atpReqCode 0x40
  1107. #endif
  1108. #ifndef atpRspCode
  1109. # define atpRspCode 0x80
  1110. #endif
  1111. #ifndef atpEOM
  1112. # define atpEOM 0x10
  1113. #endif
  1114.  
  1115. private int
  1116. zip_atp_handler(port, ddp, data, datalen)
  1117. PORT_T port;
  1118. DDP *ddp;
  1119. byte *data;
  1120. int datalen;
  1121. {
  1122.   int paranoia = FALSE;
  1123.   DDP sddp;
  1124.   ATP *atp;            /* pointer to atp header */
  1125.   zipUserBytes *zub;        /* pointer to zip user byte */
  1126.   char data_buf[ddpMaxData];    /* data buffer */
  1127.   char *p;            /* pointer to data */
  1128.   int ps = ddpMaxData;        /* room left in buffer */
  1129.   int sidx;            /* for getzonelist */
  1130.   int count, t, i;
  1131.   struct chain *zp;
  1132.  
  1133.   if (datalen < sizeof(ATP))
  1134.     return;
  1135.   bcopy((caddr_t)data, (caddr_t)data_buf, sizeof(ATP)); /* get bytes */
  1136.   atp = (ATP *)data_buf;    /* should be aligned */
  1137.   p = data_buf + sizeof(ATP);    /* point to data */
  1138.   ps -= sizeof(ATP);
  1139.   /* control must hold request and only request */
  1140.   if ((atp->control & atpCodeMask) != atpReqCode)
  1141.     return;
  1142.   /* bitmap should ask for at least one packet */
  1143.   if ((atp->bitmap & 0x1) == 0)
  1144.     return;
  1145.   zub = (zipUserBytes *)&atp->userData;
  1146.   if (paranoia && zub->zip_zero != 0)
  1147.     return;
  1148.   zub->zip_zero = 0;        /* ensure */
  1149.   switch (zub->zip_cmd) {
  1150.   case zip_GetMyZone:
  1151.     if (paranoia && ntohs(zub->zip_index) != 0)
  1152.       return;
  1153.     count = 1;
  1154.     zub->zip_cmd = 0;        /* zero because gmz */
  1155.     /* return zone of source network */
  1156.     /* if given network is 0 (mynet), use that of port */
  1157.     { struct route_entry *re;
  1158.       if (ddp->srcNet == 0) {
  1159.     if ((re = route_find(PORT_DDPNET(port))) == NULL)
  1160.       return;
  1161.       } else {
  1162.     if ((re = route_find(ddp->srcNet)) == NULL)
  1163.       return;
  1164.       }
  1165.       if (re->re_zonep == NULL)
  1166.     return;
  1167.       /* no way we could fill up buffer */
  1168.       { int zl;
  1169.     zl = 1 + *re->re_zonep;
  1170.     bcopy((caddr_t)re->re_zonep, (caddr_t)p, zl);
  1171.     ps -= zl;
  1172.       }
  1173.     }
  1174.     break;
  1175.   case zip_GetZoneList:
  1176.     sidx = ntohs(zub->zip_index);
  1177.     sidx--;            /* 1 is start */
  1178.     /* move through zonelist, decrementing sidx as we find a filled slot */
  1179.     /* a zone name may be sent more than once if we get an incoming zone */
  1180.     /* between GZL commands */
  1181.     /* move through sidx items */
  1182.     for (zp = zonelist; zp && sidx ; zp = zp->c_next)
  1183.       sidx--;
  1184.     if (sidx)            /* no more zones */
  1185.       break;
  1186.     /* i already set, zp already set */
  1187.     /* assume LastFlag */
  1188.     zub->zip_cmd = 0xff;    /* set every bit because not sure */
  1189.                 /* which bit is lastflag */
  1190.     count = 0;
  1191.     while (zp) {
  1192.       byte *znp = (byte *)zp->c_data; /* get zone name */
  1193.       t = znp[0] + 1;        /* get length of zone name */
  1194.       if ((ps - t) < 0) {
  1195.     zub->zip_cmd = 0;    /* clear lastflag: one remains */
  1196.     break;
  1197.       }
  1198.       bcopy((caddr_t)znp, (caddr_t)p, t); /* copy data */
  1199.       count++;            /* bump count */
  1200.       ps -= t;            /* reduce available data */
  1201.       p += t;            /* move data pointer */
  1202.       zp = zp->c_next;        /* move to next zone */
  1203.     }
  1204.     zub->zip_index = count;    /* set count */
  1205.     break;
  1206.   default:            /* bad type, this is NOT paranoia */
  1207.     return;
  1208.   }
  1209.   zub->zip_index = htons(count); /* set count */
  1210.   atp->control = atpRspCode|atpEOM;
  1211.   atp->bitmap = 0;        /* sequence 0 */
  1212.   /* tid already set */
  1213.   sddp.dstNet = ddp->srcNet;
  1214.   sddp.dstNode = ddp->srcNode;
  1215.   sddp.dstSkt = ddp->srcSkt;
  1216.   sddp.srcSkt = ddp->dstSkt;
  1217.   sddp.type = ddpATP;
  1218.   ddp_output(NULL, &sddp, (byte *)data_buf, ddpMaxData - ps);
  1219. }
  1220.  
  1221. /* keep zone name in a linked list */
  1222.  
  1223. /* take a zone pstring and duplicate it -- make sure null terminated */
  1224. private caddr_t
  1225. zone_alloc(p)
  1226. byte *p;
  1227. {
  1228.   int len = (int)*p;        /* get length */
  1229.   struct chain *cnode;
  1230.   byte *r;
  1231.  
  1232.   if ((cnode = (struct chain *)malloc(sizeof(struct chain))) == NULL)
  1233.     return(NULL);
  1234.   m_cnode++;
  1235.   if (p == NULL)        /* translate NULL string */
  1236.     p = '\0';
  1237.   r = (byte *)malloc(len+2);    /* one for null, one for lenth */
  1238.   if (r == NULL) {
  1239.     free(cnode);
  1240.     return(NULL);
  1241.   }
  1242.   m_zone++;
  1243.   bcopy(p, r, len+1);        /* copy in data */
  1244.   r[len+1] = '\0';        /* make sure tied off */
  1245.   cnode->c_next = zonelist;    /* link next to head */
  1246.   zonelist = cnode;        /* link link to this */
  1247.   cnode->c_data = (caddr_t)r;    /* copy in data */
  1248.   return((caddr_t)cnode);
  1249. }
  1250.  
  1251. private int
  1252. pstrc(p,s)
  1253. byte *p, *s;
  1254. {
  1255.   int r = (*p - *s);
  1256.   if (r)
  1257.     return(r);
  1258.   return(bstrcmp(p+1, s+1, *p));
  1259. }
  1260.  
  1261. private int
  1262. zone_compare(s,cnode)
  1263. byte *s;
  1264. struct chain *cnode;
  1265. {
  1266.   return(pstrc(s, cnode->c_data));
  1267. }
  1268.  
  1269. private u_int
  1270. zone_compress(p)
  1271. byte *p;
  1272. {
  1273.   u_int r = 0;
  1274.   int i = (int) *p++;
  1275.   /* add in p, but keep rotating r */
  1276.   while (i--)
  1277.     r = ((r>>1)|(r<<31)) + *p++;
  1278.   return(r);
  1279. }
  1280.  
  1281. private void
  1282. zone_init()
  1283. {
  1284.   zone_hash_table = h_new(HASH_POLICY_CHAIN, HASH_TYPE_MULTIPLICATIVE,
  1285.               NZONES, zone_compare, zone_alloc, zone_compress,
  1286.               NULL, NULL, NULL);
  1287.   zonelist = NULL;
  1288.   ddp_open(zipZIS, zip_handler);
  1289. }
  1290.  
  1291. export byte *
  1292. zone_find(name, insert)
  1293. byte *name;
  1294. int insert;
  1295. {
  1296.   int b, d;
  1297.   struct chain *cnode;
  1298.   cnode = (struct chain *)h_operation(insert ? HASH_OP_INSERT : HASH_OP_MEMBER,
  1299.               zone_hash_table, name, -1,-1,&d,&b);
  1300.   if (cnode == NULL)
  1301.     return(NULL);
  1302.   log(LOG_LOTS, "%s for %s [%d,%d]\n",
  1303.       insert ? "insert" : "lookup", cnode->c_data+1, b,d);
  1304.   return((byte *)cnode->c_data); /* return data */
  1305. }
  1306.  
  1307.  
  1308. private void
  1309. rtmp_format_hash_stats(fd, s)
  1310. FILE *fd;
  1311. struct hash_statistics *s;
  1312. {
  1313.   fprintf(fd, "\t%d lookups since last rehash, average distance %.02f\n",
  1314.       s->hs_lnum, s->hs_lnum ? ((float)s->hs_lsum / s->hs_lnum) : 0.0);
  1315.   fprintf(fd, "\t%d lookups total, average distance %.02f\n",
  1316.       s->hs_clnum, s->hs_clnum ? ((float)s->hs_clsum / s->hs_clnum) : 0.0);
  1317. }
  1318.  
  1319. export void
  1320. rtmp_dump_stats(fd)
  1321. FILE *fd;
  1322. {
  1323.   putc('\n', fd);
  1324.   fprintf(fd, "Hash table statistics for zone lookups\n");
  1325.   rtmp_format_hash_stats(fd, h_statistics(zone_hash_table));
  1326.   fprintf(fd, "\nHash table statistics for routing table lookups\n");
  1327.   rtmp_format_hash_stats(fd, h_statistics(route_htable_handle));
  1328.   putc('\n', fd);        /* output cr */
  1329.   fprintf(fd,"%d routes, %d bridge nodes allocated\n", m_route, m_bnode);
  1330.   fprintf(fd,"%d zones, %d zone chain nodes allocated\n", m_zone, m_cnode);
  1331.   putc('\n', fd);        /* output cr */
  1332. }
  1333.  
  1334. export void
  1335. rtmp_dump_table(fd)
  1336. FILE *fd;
  1337. {
  1338.   register struct route_entry *re;
  1339.  
  1340.   fprintf(fd, "Routing table dump\n");
  1341.   for (re = routes; re ; re = re->re_next) 
  1342.     if (re->re_state)
  1343.       rtmp_dump_entry_to_file(fd, re);
  1344.   putc('\n', fd);
  1345. }
  1346.